﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using EnvDTE;
using System.Diagnostics;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Drawing;

namespace SolutionNavigateAddIn
{
    public class SolutionWatcher
    {
        public static string[] Extensions = new string[] { ".sln", ".csproj", ".vbproj", ".vcproj", ".vjproj" };

        static SolutionWatcher()
        {
            
        }

        private static Window2 SolutionWindow;
        private static CommandBarComboBox comboBox;
        private static Dictionary<string, int> projectIndex = new Dictionary<string, int>();
        private static Dictionary<string, Project> projectValues = new Dictionary<string, Project>();

        public static void Activate()
        {
            SnEvents.LoadStateChanged += (SnEvents_LoadStateChanged);
            SnEvents.SolutionStateChanged += (SnEvents_SolutionStateChanged);
            SnEvents.ProjectChanged += (SnEvents_ProjectChanged);
            SnEvents.OpenComboBoxCommandExecuted += (SnEvents_OpenComboBoxCommandExecuted);
            SnEvents.SelectionChanged += SnEvents_SelectionChanged;

            if (!string.IsNullOrEmpty(SnEnvironment.MainDTE.CommandLineArguments))
            {
                foreach (string extension in Extensions)
                {
                    if (SnEnvironment.MainDTE.CommandLineArguments.Contains(extension))
                    {
                        InitializeComboBox();
                        break;
                    }
                }
            }
        }

        static void SnEvents_SelectionChanged(object sender, EventArgs e)
        {
            foreach (SelectedItem item in SnEnvironment.MainDTE.SelectedItems)
            {
                Project project;
                if (TryGetProject(item, out project)
                    ||
                    TryGetContainingProject(item, out project)
                    ||
                    TryGetActiveSolutionProjects(out project))
                {
                    SelectProject(project);
                    break;
                }
            }
        }

        static void SnEvents_OpenComboBoxCommandExecuted(object sender, EventArgs e)
        {
            if (comboBox != null && SolutionWindow != null)
            {
                SolutionWindow.SetFocus();
                comboBox.SetFocus();
            }
        }

        static void SnEvents_ProjectChanged(object sender, SnEvents.ProjectEventArgs e)
        {
            ComboBoxFill();
        }

        static void SnEvents_SolutionStateChanged(object sender, SnEvents.SolutionEventArgs e)
        {
           if (e.State == SnEvents.SolutionEventArgs.SolutionState.Open)
           {
               if (comboBox == null)
                   InitializeComboBox();
               else
                   comboBox.Visible = true;
               ComboBoxFill();
           }
           else
           {
               Clear();
               comboBox.Visible = false;
           }
        }

        static void SnEvents_LoadStateChanged(object sender, SnEvents.ReloadUnloadEventArgs e)
        {
            ComboBoxFill();
        }

        private static bool TryGetProject(SelectedItem item, out Project project)
        {
            try
            {
                if (item.Project != null)
                {
                    project = item.Project;
                    return true;
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(
                    string.Format(
                        "Method SolutionWatcher.TryGetProject(SelectedItem, out Project) generate exception: {0}",
                        e.Message));
            }

            project = null;
            return false;
        }

        private static bool TryGetContainingProject(SelectedItem item, out Project project)
        {
            try
            {
                if (item.ProjectItem != null
                    &&
                    item.ProjectItem.ContainingProject != null)
                {
                    project = item.ProjectItem.ContainingProject;
                    return true;
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(
                    string.Format(
                        "Method SolutionWatcher.TryGetContainingProject(SelectedItem, out Project) generate exception: {0}",
                        e.Message));
            }

            project = null;
            return false;
        }

        private static bool TryGetActiveSolutionProjects(out Project project)
        {
            try
            {
                IEnumerable enumerable = (SnEnvironment.MainDTE.ActiveSolutionProjects as IEnumerable);
                if (enumerable != null)
                {
                    IEnumerator enumerator = enumerable.GetEnumerator();
                    while (enumerator.MoveNext())
                    {
                        project = enumerator.Current as Project;
                        if (project != null)
                            return true;
                    }
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(
                    string.Format(
                        "Method SolutionWatcher.TryGetActiveSolutionProjects(out Project) generate exception: {0}",
                        e.Message));
            }

            project = null;
            return false;
        }

        private static int iterator = 1;
       
        private static void Clear()
        {
            comboBox.Clear();
            iterator = 1;
            projectIndex.Clear();
            projectValues.Clear();
        }

        private static void ComboBoxFill()
        {
            Clear();

            List<Project> projects = new List<Project>();

            foreach (Project project in SnEnvironment.MainDTE.Solution.Projects)
            {
                ProcessingProject(project, projects);
            }

            projects.Sort(new ProjectComparer());

            int comboBoxSize = 100;
            comboBox.Width = comboBoxSize;
            foreach (Project project in projects)
            {
                comboBox.AddItem(project.Name, iterator);

                int projectNameSize = CalculateProjectNameSize(project.Name);
                
                if (projectNameSize > comboBoxSize)
                {
                    comboBoxSize = projectNameSize;
                    comboBox.Width = comboBoxSize;
                }

                projectIndex[project.Name] = iterator;
                projectValues[project.Name] = project;
                iterator++;
            }
        }

        private static int CalculateProjectNameSize(string name)
        {
            return TextRenderer.MeasureText(name, new Font("Courier", 8)).Width;
        }

        private static void ProcessingProject(Project project, List<Project> projects)
        {
            try
            {
                switch (project.Kind)
                {
                    case WatcherConstant.ProjectCSharp:
                    case WatcherConstant.ProjectVB:
                    case WatcherConstant.ProjectCPP:
                    case WatcherConstant.ProjectJSharp:
                    case WatcherConstant.ProjectWeb:
                        {
                            projects.Add(project);
                            break;
                        }
                    case WatcherConstant.SolutionFolder:
                        {
                            foreach (ProjectItem item in project.ProjectItems)
                            {
                                if (item.Kind == WatcherConstant.ProjectItem)
                                {
                                    if (item.SubProject != null)
                                    {
                                        ProcessingProject(item.SubProject, projects);
                                    }
                                }
                            }
                            break;
                        }
                    case WatcherConstant.Misc:
                        {
                            break;
                        }
                    case WatcherConstant.UnloadProject:
                        {
                            /*projects.Add(project);*/
                            break;
                        }
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(string.Format("Method SolutionWatcher.ProcessingProject generate exception: {0}",
                                              e.Message));
            }

        }

        private static void InitializeComboBox()
        {
            try
            {
                foreach (Window window in SnEnvironment.MainDTE.Windows)
                {
                    if (window.Caption == WatcherConstant.SolutionExplorer)
                    {
                        SolutionWindow = window as Window2;

                        if (SolutionWindow != null)
                        {
                            CommandBars cb = SolutionWindow.CommandBars as CommandBars;
                            if (cb != null)
                            {
                                foreach (CommandBar bar in cb)
                                {
                                    if (bar.Name == WatcherConstant.SolutionExplorerBar)
                                    {

                                        if ((bar.Protection & MsoBarProtection.msoBarNoCustomize) > 0)
                                            bar.Protection ^= MsoBarProtection.msoBarNoCustomize;

                                        comboBox = (CommandBarComboBox)
                                                   bar.Controls.Add(MsoControlType.msoControlComboBox, 1,
                                                                    Type.Missing,
                                                                    Type.Missing, false);
                                        comboBox.Width = 100;
                                        comboBox.Change += (comboBox_Change);
                                        
                                    }
                                }

                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Trace.TraceError(string.Format("Method SolutionWatcher.InitializeComboBox generate exception: {0}", e.Message));
            }
        }

        static void comboBox_Change(CommandBarComboBox Ctrl)
        {
            foreach (KeyValuePair<string, int> pair in projectIndex)
            {
                if (Ctrl.ListIndex == pair.Value)
                {
                    try
                    {
                        Project project;
                        if (projectValues.TryGetValue(pair.Key,out project))
                        {
                            UIHierarchyItem uielement = UIHierarchyManager.GetUI(project);

                            if (uielement != null)
                            {
                                uielement.Select(vsUISelectionType.vsUISelectionTypeSetCaret |
                                                           vsUISelectionType.vsUISelectionTypeSelect);
                                uielement.UIHierarchyItems.Expanded = true;

                            }
                        }
                    }
                    catch (Exception)
                    {
                        
                        
                    }
                    
                }
            }
        }

        private static void SelectProject(Project project)
        {
            int index;
            if (projectIndex.TryGetValue(project.Name,out index))
            {
                comboBox.ListIndex = index;
            }
        }

    }
}
